package cn.qfei.qiyuesuo.interceptor;

import com.alibaba.fastjson2.JSONObject;
import com.lark.oapi.core.response.BaseResponse;
import com.lark.oapi.okhttp.*;
import com.lark.oapi.okio.Buffer;
import com.lark.oapi.okio.BufferedSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays;

/**
 * 飞书API重试拦截器
 *
 * @author guanfeng
 * @date 2023/3/28 11:38
 */
@Slf4j
public class FeishuApiRetryInterceptor implements Interceptor {

    private int maxRetry;//最大重试次数
    private int retryNum = 0;//假如设置为3次重试的话，则最大可能请求4次（默认1次+3次重试）
    private static int[] no_need_retry_error_codes = new int[]{5000, 11247, 90217, 90226, 91206, 190005, 232019, 1000004, 1000005, 99991400};

    public FeishuApiRetryInterceptor(int maxRetry) {
        this.maxRetry = maxRetry;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = null;
        do {
            try {
                response = chain.proceed(request);
            } catch (IOException e) {
                log.error("请求地址[[{}]{}]出现异常", request.method(), request.url().url(), e);
            }
            boolean requestSuccess = false;
            boolean responseContentSuccess = true;
            boolean specialRequest = false;
            if (response != null) {
                requestSuccess = response.isSuccessful();
                int httpCode = response.code();
                if (HttpStatus.TOO_MANY_REQUESTS.value() == httpCode) {//触发限速
                    specialRequest = true;
                } else {
                    String bodyStr = getBody(response);
                    log.debug("请求地址[[{}]{}]，响应体[{}]", request.method(), request.url().url(), bodyStr);
                    if (StringUtils.hasText(bodyStr)) {
                        try {
                            BaseResponse rsp = JSONObject.parseObject(bodyStr, BaseResponse.class);
                            int feishuErrorCode = rsp.getCode();
                            if (isNeedRetryForErrorCode(feishuErrorCode)) {
                                specialRequest = true;
                            } else {
                                responseContentSuccess = rsp.success();
                            }
                        } catch (Exception e) {
                        }
                    }
                }
            }
            if ((!requestSuccess || !responseContentSuccess) && !specialRequest && retryNum < maxRetry) {
                retryNum++;
                log.debug("请求地址[[{}]{}]，当前重试次数[{}]", request.method(), request.url().url(), retryNum);
                if (response != null) {
                    response.close();
                }
            } else {
                break;
            }
        } while (true);
        return response;
    }

    private boolean isNeedRetryForErrorCode(int code) {
        return Arrays.stream(no_need_retry_error_codes).anyMatch(c -> c == code);
    }

    private String getBody(Response rsp) throws IOException {
        ResponseBody body = rsp.body();
        long contentLength = body.contentLength();
        Headers headers = rsp.headers();
        if (bodyEncoded(headers) || contentLength <= 0) {
            return null;
        }
        MediaType contentType = body.contentType();
        Charset charset = StandardCharsets.UTF_8;
        if (contentType != null) {
            try {
                charset = contentType.charset(StandardCharsets.UTF_8);
            } catch (UnsupportedCharsetException e) {
                log.error("不支持的请求编码[{}]", e.getCharsetName());
                return null;
            }
        }
        BufferedSource src = body.source();
        src.request(Long.MAX_VALUE);
        Buffer buffer = src.getBuffer();
        return buffer.clone().readString(charset);
    }

    private boolean bodyEncoded(Headers headers) {
        String contentEncoding = headers.get("Content-Encoding");
        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
    }
}
